
package com.kube.gateway.core.router;

import com.kube.gateway.core.router.domain.dto.RouteDefinitionDto;
import com.kube.gateway.core.router.manager.RouterManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;

@Service
public class DynamicRouteService implements ApplicationEventPublisherAware {
    private static final Logger log = LoggerFactory.getLogger(DynamicRouteService.class);

    @Resource
    private RouteDefinitionWriter routeDefinitionWriter;

    private ApplicationEventPublisher publisher;

    @Autowired
    private RouterManager routerManager;

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.publisher = applicationEventPublisher;
    }

    /**
     * 增加路由
     */
    public Boolean save(RouteDefinitionDto definition) {
        if (routerManager.saveRoute(definition)) {
            try {
                routeDefinitionWriter.save(Mono.just(build(definition))).subscribe();
                this.publisher.publishEvent(new RefreshRoutesEvent(this));
                return Boolean.TRUE;
            } catch (Exception e) {
                log.error("save failure", e);
            }
        }
        return Boolean.FALSE;
    }

    /**
     * 更新路由
     */
    public Boolean update(RouteDefinitionDto definition) {
        if (routerManager.saveRoute(definition)) {
            try {
                this.routeDefinitionWriter.delete(Mono.just(definition.getName()));
                this.routeDefinitionWriter.save(Mono.just(build(definition))).subscribe();
                this.publisher.publishEvent(new RefreshRoutesEvent(this));
                return Boolean.TRUE;
            } catch (Exception e) {
                log.error("update failure", e);
                return Boolean.FALSE;
            }
        }
        return Boolean.FALSE;
    }

    /**
     * 更新路由
     */
    public Boolean updateList(List<RouteDefinitionDto> routeDefinitions) {
        routeDefinitions.forEach(this::update);
        return Boolean.TRUE;
    }

    /**
     * 删除路由
     */
    public Boolean delete(String id) {
        try {
            this.routeDefinitionWriter.delete(Mono.just(id));
            return Boolean.TRUE;
        } catch (Exception e) {
            log.error("delete failure", e);
            return Boolean.FALSE;
        }
    }

    private RouteDefinition build(RouteDefinitionDto definitionDto) {
        RouteDefinition routeDefinition = new RouteDefinition();
        routeDefinition.setId(definitionDto.getName());
        routeDefinition.setUri(URI.create(definitionDto.getUri()));
        if (!CollectionUtils.isEmpty(definitionDto.getPredicateDtoList())) {
            List<PredicateDefinition> predicates = new ArrayList<>();
            definitionDto.getPredicateDtoList().forEach(it->{
                PredicateDefinition predicateDefinition = new PredicateDefinition();
                predicateDefinition.setName(it.build());
                predicates.add(predicateDefinition);
            });
            routeDefinition.setPredicates(predicates);
        }
        if (!CollectionUtils.isEmpty(definitionDto.getFilterDtoList())){
            List<FilterDefinition> filters = new ArrayList<>();
            definitionDto.getFilterDtoList().forEach(it->{
                FilterDefinition filterDefinition = new FilterDefinition();
                filterDefinition.setName(it.getName());
                filters.add(filterDefinition);
            });
            routeDefinition.setFilters(filters);
        }
        return routeDefinition;
    }
}
